/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package se.bth.ke.firstfriend.scheduled.rating;

import java.util.List;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import se.bth.ke.firstfriend.model.Article;
import se.bth.ke.firstfriend.model.Rating;

/**
 *
 * @author markus, nico
 */
@Component
public class RatingCalculator {

    @PersistenceContext
    private EntityManager em;

    private ArticleRater rater = new ArticleRater();

    private static final Logger LOGGER = Logger.getLogger("se.bth.ke.firstfriend.scheduled.rating.RatingCalculator");

    /**
     * Calculates ratings and relevancies.
     * This task will be scheduled every day at 1:01 AM.
     */
    @Transactional
    @Scheduled(cron="1 1 1 * * ?")
    public void calculateRaitingsAndRelevancies() {

        LOGGER.info("Updating rating and relevance for articles ...");
        
        List<Article> articles = getAllArticles();
        
        calculateAverageRatingsFor(articles);
        calculateRelevancesFor(articles);

        LOGGER.info("Updated rating and relevance for articles. Done.");
    }

    private void calculateAverageRatingsFor(List<Article> articles) {
        for (Article article: articles) {
            List<Rating> currentRatings = getCurrentRatingsFor(article);
            double rating = rater.calculateAverageRating(article, currentRatings);
            article.setRating(rating);
        }
    }

    private void calculateRelevancesFor(List<Article> articles) {
        rater.setGlobalRatingAverage(getGlobalAverage(articles));

        for (Article article : articles) {
            List<Rating> currentRatings = getCurrentRatingsFor(article);
            double relevance = rater.calculateRelevance(article, currentRatings);
            article.setRelevance(relevance);
        }
    }

    private List<Article> getAllArticles() {
        return em.createQuery("SELECT a FROM Article a JOIN FETCH a.topics")
                 .getResultList();
    }

    private List<Rating> getCurrentRatingsFor(Article article) {
        return em.createNativeQuery("SELECT * FROM "
                    + "(SELECT * FROM rating ri ORDER BY ri.created_at DESC) AS q1 "
                    + "WHERE q1.article = :article "
                    + "GROUP BY q1.user, q1.article", Rating.class)
                 .setParameter("article", article.getId())
                 .getResultList();
    }

    private double getGlobalAverage(List<Article> articles) {
        double sum = 0.0;
        if (articles == null || articles.isEmpty()) {
            return sum;
        }

        for (Article article : articles) {
            sum += article.getRating();
        }
        return sum / (double) articles.size();
    }
}
